package com.superbytecode.cloud.common.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.assertj.core.util.Sets;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * All rights Reserved, Designed By www.super-master.com
 *
 * @ProjectName: beacon-cloud
 * @Package: com.superbytecode.cloud.common.utils
 * @ClassName: TreeUtil
 * @Description: [树形结构工具类]
 * <p> </p>
 * @Author: [Lance Ting]
 * @Date: 2024/10/22 12:22
 * @Version: V1.0
 * @Copyright: 2024 www.super-master.com Inc. All rights reserved.
 * TODO: 注意,本文件Lance Ting所作,如果转载或使用请标明具体出处!
 **/
@UtilityClass
public class TreeUtil {
    // TODO: 根节点值, 根据据图业务设置，默认为：0
    final private String PARENT_NODE_VALUE = "0";

    /**
     * 集合转树结构
     *
     * @param collection 目标集合
     * @param clazz      集合元素类型
     * @param <T>        约束类型
     * @return 转换后的树形结构
     * @throws NoSuchFieldException Field缺失异常
     */
    public <T> Collection<T> toTree(final Collection<T> collection, Class<T> clazz) throws NoSuchFieldException {
        return toTree(collection, clazz, null, null, null);
    }

    /**
     * 集合转树结构
     *
     * @param collection        目标集合
     * @param fieldName         当前节点编号字段名称
     * @param parentFieldName   父节点编号字段名称
     * @param childrenFieldName 子节点集合属性名称
     * @param clazz             集合元素类型
     * @param <T>               约束类型
     * @return 转换后的树形结构
     * @throws NoSuchFieldException Field缺失异常
     */
    public <T> Collection<T> toTree(final Collection<T> collection, Class<T> clazz, String fieldName, String parentFieldName, String childrenFieldName) throws NoSuchFieldException {
        // 目标集合为空,直接返回一个空树
        if (CollectionUtil.isEmpty(collection)) {
            return null;
        }
        // 初始化节点以及依赖字段名称
        fieldName = loadBeanFieldName(fieldName, "id");
        parentFieldName = loadBeanFieldName(parentFieldName, "parent");
        childrenFieldName = loadBeanFieldName(childrenFieldName, "children");
        // 从当前对象或其父类，以及设置为可访问
        Field idField = loadBeanField(fieldName, clazz);
        idField.setAccessible(true);
        Field parentField = loadBeanField(parentFieldName, clazz);
        parentField.setAccessible(true);
        Field childrenField = loadBeanField(childrenFieldName, clazz);
        childrenField.setAccessible(true);
        // 找出所有的根节点
        Stream<T> stream = collection.stream().filter(item -> isRootNode(FunctionalUtil.sure(() -> parentField.get(item))));
        // 初始化根节点集合, 支持 Set 和 List
        Collection<T> roots = collection.getClass().isAssignableFrom(List.class)//
                ? stream.collect(Collectors.toList()) //
                : stream.collect(Collectors.toSet());
        // 从目标集合移除所有根节点
        collection.removeAll(roots);
        // 遍历根节点, 依次添加子节点
        roots.forEach(root -> FunctionalUtil.sure(() -> addChildNode(root, collection, idField, parentField, childrenField)));
        // 关闭可访问
        idField.setAccessible(false);
        parentField.setAccessible(false);
        childrenField.setAccessible(false);
        return roots;
    }

    /**
     * 为目标节点添加子节点
     *
     * @param node          目标节点
     * @param collection    目标集合
     * @param field         当前节点字段
     * @param parentField   父节点字段
     * @param childrenField 子节点字段
     * @param <T>           泛型
     */
    private <T> void addChildNode(@NotNull T node, @NotNull Collection<T> collection, @NotNull Field field,//
                                  @NotNull Field parentField, @NotNull Field childrenField) throws IllegalAccessException {
        @SuppressWarnings("unchecked") Collection<T> children = (Collection<T>) childrenField.get(node);
        // 如果子节点的集合为null, 初始化子集合
        children = CollectionUtil.isEmpty(children) ? children//
                : collection.getClass().isAssignableFrom(List.class) ? Lists.newArrayList() : Sets.newHashSet();
        Object id = field.get(node);
        for (T item : collection) {
            Object parent = parentField.get(item);
            if (!Objects.equals(id, parent)) { // 当前节点id不等父节点id，表示不在一层，跳过
                continue;
            }
            // 将当前节点添加到目标节点的孩子节点
            children.add(item);
            // 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失
            // 所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
            childrenField.set(node, children);
            // 递归添加孩子节点
            addChildNode(item, collection, field, parentField, childrenField);
        }
    }

    private String loadBeanFieldName(String fieldName, String defaultFieldName) {
        return StringUtils.isNoneBlank(fieldName) ? fieldName : defaultFieldName;
    }

    /**
     * 从当前对象或其父类
     *
     * @param fieldName 属性名称
     * @param clazz     当前对象
     * @param <T>       限制类型
     * @return Field对象
     * @throws NoSuchFieldException Field异常
     */
    private <T> Field loadBeanField(String fieldName, Class<T> clazz) throws NoSuchFieldException {
        Field field;
        try {
            field = clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            field = clazz.getSuperclass().getDeclaredField(fieldName);
        }
        return field;
    }

    /**
     * 判断是否是根节点
     * <p>
     * 判断逻辑: 父节点编号为空或为 0, 则认为是根节点. 此处的判断应根据具体业务数据而定.
     * </p>
     *
     * @param parentId 父节点编号
     * @return 是否是根节点
     */
    private boolean isRootNode(Object parentId) {
        if (VerifyUtil.isNull(parentId)) {
            return true;
        }
        return Objects.equals(PARENT_NODE_VALUE, String.valueOf(parentId));
    }
}